import static org.junit.Assert.*;

import java.util.List;

import org.junit.Test;


public class BattleshipModelTest {

	@Test
	/**
	 * Test the p1Attack and p2Attack methods to see if an attack on a cell that contains a hit returns "HIT".
	 */
	public void attackHitCell() {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "A1", "A5", 0);
		model.placeShip(false, "B1", "B5", 0);
		assertEquals("Return if the player 1's attack was a hit. Expected result: ", "HIT", model.p1Attack("B1"));
		assertEquals("Return if the player 2's attack was a hit. Expected result: ", "HIT", model.p2Attack("A1"));
	}
	
	@Test
	/**
	 * Test the p1Attack and p2Attack methods to see if an attack on an unoccupied cell returns "MISS".
	 */
	public void attackMissedCell() {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "A1", "A5", 0);
		model.placeShip(false, "B1", "B5", 0);
		assertEquals("Return if the player 1's attack was a hit. Expected result: ", "MISS", model.p1Attack("A1"));
		assertEquals("Return if the player 2's attack was a hit. Expected result: ", "MISS", model.p2Attack("B1"));
	}

	@Test
	/**
	 * Test the p1Attack and p2Attack methods to see if an attack on a previously attacked cell returns "INVALID".
	 */
	public void attackInvalidCell() {
		BattleshipModel model = new BattleshipModel();
		model.addUsedCell(model.getPlayer(true), "A1");
		model.addUsedCell(model.getPlayer(false), "B1");
		assertEquals("Return if the player 1's attack was a hit. Expected result: ", "INVALID", model.p1Attack("A1"));
		assertEquals("Return if the player 2's attack was a hit. Expected result: ", "INVALID", model.p2Attack("B1"));
	}
	
	@Test
	/**
	 * Test the p1Attack and p2Attack methods to see if an attack on a hit and sunken ship returns "SUNK".
	 */
	public void attackHitAndSinkCell() {
		BattleshipModel model = new BattleshipModel();
		String result = "";
		String result2 = "";
		model.placeShip(true, "A1", "A2", 4);
		model.placeShip(false, "B1", "B2", 4);
		result = model.p1Attack("B1");
		result2 = model.p2Attack("A1");
		assertEquals("Return if the player 1's attack was a hit and sunk the ship. Expected result: ", "Destroyer", model.p1Attack("B2"));
		assertEquals("Return if the player 2's attack was a hit and sunk the ship. Expected result: ", "Destroyer", model.p2Attack("A2"));	
		
	}
	
	@Test
	/**
	 * Test the addShipCells method to see to add cells to the Aircraft Carrier. 
	 */
	public void checkShipAddedCarrier() {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "A1", "A5", 0);
		assertEquals("Return the cells that were added to the aircraft carriers ship cells. Expected results: ", "[A1, A2, A3, A4, A5]", model.getSurvivingCells(true).toString());
	}
	
	@Test
	/**
	 * Test the addShipCells method to see to add cells to the Battleship. 
	 */
	public void checkShipAddedBattleship() {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "A1", "A4", 1);
		assertEquals("Return the cells that were added to the battleships cells. Expected results: ", "[A1, A2, A3, A4]", model.getSurvivingCells(true).toString());
	}
	
	/**
	 * Test the addShipCells method to see to add cells to the cruiser. 
	 */
	@Test
	public void checkShipAddedCruiser() {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "A1", "A3", 2);
		assertEquals("Return the cells that were added to the crusiers ship cells. Expected results: ", "[A1, A2, A3]", model.getSurvivingCells(true).toString());
	}
	
	@Test
	/**
	 * Test the addShipCells method to see to add cells to the submarine. 
	 */
	public void checkShipAddedSubmarine() {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "A1", "A3", 3);
		assertEquals("Return the cells that were added to the submarines cells. Expected results: ", "[A1, A2, A3]", model.getSurvivingCells(true).toString());
	}
	
	@Test
	/**
	 * Test the addShipCells method to see to add cells to the destroyer. 
	 */
	public void checkShipAddedDestroyer() {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "A1", "A2", 2);
		assertEquals("Return the cells that were added to the destroyers cells. Expected results: ", "[A1, A2]", model.getSurvivingCells(true).toString());
	}
	
	@Test
	/**
	 * Test the getEndpoints function using a start point that has two possible end points.
	 */
	public void getEndpointsWithTwoValidEndPoints() {
		BattleshipModel model = new BattleshipModel();
		List<String> endpts = model.getEndpoints(true, "A1", 0);
		assertEquals("Return the two valid endpoints. Expected results: ", "[51, 15]", endpts.toString());
	}
	
	@Test
	/**
	 * Test the getEndpoints function using a start point that has one possible end point.
	 */
	public void getEndpointsWithOccupiedSpace() {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "B1", "B6", 0);
		List<String> endpts = model.getEndpoints(true, "A1", 1);
		assertEquals("Return the valid endpoint when a ship is in the way. Expected results: ", "[14]", endpts.toString());
	}
	
	@Test
	/**
	 * Test the getEndpoints function using a start point on the right border.
	 */
	public void getEndpointsOnRightBorder() {
		BattleshipModel model = new BattleshipModel();
		List<String> endpts = model.getEndpoints(true, "A10", 0);
		assertEquals("Return the valid endpoint when the start is on the right border. Expected results: ", "[510]", endpts.toString());
	}
	
	@Test
	/**
	 * Test the getEndpoints function using a start point on the bottom border.
	 */
	public void getEndpointsOnBottomBorder() {
		BattleshipModel model = new BattleshipModel();
		List<String> endpts = model.getEndpoints(true, "J1", 0);
		assertEquals("Return the valid endpoint when the start is on the bottom. Expected results: ", "[105]", endpts.toString());
	}
	
	@Test
	/**
	 * Test the getEndpoints function using a start point that has no valid end points.
	 */
	public void getEndpointsWithNoValidPoints() {
		BattleshipModel model = new BattleshipModel();
		List<String> endpts = model.getEndpoints(true, "J10", 0);
		assertEquals("Return the valid endpoint array when no valid endpoints are available. Expected results: ", "[]", endpts.toString());
	}
	
	@Test
	/**
	 * Test the getName method to get the name of the aircraft carrier.
	 */
	public void getCarrierName() {
		BattleshipModel model = new BattleshipModel();
		assertEquals("Return the name of the aircraft carrier. Expected results: ", "Aircraft Carrier", model.getShipName(true, 0));

	}
	
	@Test
	/**
	 * Test the getName method to get the name of the battleship.
	 */
	public void getBattleshipName() {
		BattleshipModel model = new BattleshipModel();
		assertEquals("Return the name of the battleship. Expected results: ", "Battleship", model.getShipName(true, 1));
	}
	
	@Test
	/**
	 * Test the getName method to get the name of the cruiser.
	 */
	public void getCruiserName() {
		BattleshipModel model = new BattleshipModel();
		assertEquals("Return the name of the cruiser. Expected results: ", "Cruiser", model.getShipName(true, 2));
	}
	
	@Test
	/**
	 * Test the getName method to get the name of the submarine.
	 */
	public void getSubmarineName() {
		BattleshipModel model = new BattleshipModel();
		assertEquals("Return the name of the submarine. Expected results: ", "Submarine", model.getShipName(true, 3));
	}
	
	@Test
	/**
	 * Test the getName method to get the name of the destroyer.
	 */
	public void getDestroyerName() {
		BattleshipModel model = new BattleshipModel();
		assertEquals("Return the name of the destroyer. Expected results: ", "Destroyer", model.getShipName(true, 4));
	}
	
	@Test
	/**
	 * Test the isValidPlacement to check if an invalid placement cell returns false.
	 */
	public void clickedInvalidPlacementCell () {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "A1", "A5", 0);
		assertEquals("Return the result of clicking on a cell that is not occupied. Expected results: ", "false" , model.isValidPlacement(true, "A1").toString());
	}
	
	@Test
	/**
	 * Test the isValidPlacement to check if a valid placement cell returns true.
	 */
	public void clickedValidPlacementCell () {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "A1", "A5", 0);
		assertEquals("Return the result of clicking on a cell that is occupied. Expected results: ", "true" , model.isValidPlacement(true, "B2").toString());
	}
	
	@Test
	/**
	 * Test the isValidAttack to check if an invalid attack cell returns false.
	 */
	public void clickedInvalidAttackedCell () {
		BattleshipModel model = new BattleshipModel();
		model.addUsedCell(model.getPlayer(true), "A1");
		assertEquals("Return the result of clicking on a cell that has already been used. Expected results: ", "false" , model.isValidAttack(model.getPlayer(true), "A1").toString());
	}
	
	@Test
	/**
	 * Test the isValidAttack to check if a valid attack cell returns false.
	 */
	public void clickedValidAttackedCell () {
		BattleshipModel model = new BattleshipModel();
		model.addUsedCell(model.getPlayer(true), "A1");
		assertEquals("Return the result of clicking on a cell that has not already been used. Expected results: ", "true" , model.isValidAttack(model.getPlayer(false), "A1").toString());
	}
	
	@Test
	/**
	 * Test the addUsedCell method to check if attacked cell is added to the players used cells.
	 */
	public void addValidUsedCell () {
		BattleshipModel model = new BattleshipModel();
		model.addUsedCell(model.getPlayer(true), "A1");
		assertEquals("Return the used cell array after a clicked cell has been added. Expected results: ", "[A1]" , model.getPlayer(true).getUsedCells().toString());
	}
	
	@Test
	/**
	 * Test the isHit function to see if an attack on the opponents occupied cell is a hit.
	 */
	public void hitShip () {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "A1", "A5", 0);
		assertEquals("Return the result of attacking an occupied cell. Expected results: ", "true" , model.isHit(model.getPlayer(true), "A1").toString());
	}
	
	@Test
	/**
	 * Test the isHit function to see if an attack on the opponents unoccupied cell is a hit.
	 */
	public void missShip () {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "A1", "A5", 0);
		assertEquals("Return the result of attacking an unoccupied cell. Expected results: ", "false" , model.isHit(model.getPlayer(true), "J4").toString());
	}
	
	@Test
	/**
	 * Test the getSunkenShip method when the player destroys all the cells in the opponents ship.
	 */
	public void getValidSunkShip () {
		BattleshipModel model = new BattleshipModel();
		String result = "";
		model.placeShip(true, "A1", "A2", 4);
		result = model.p2Attack("A1");
		assertEquals("Return the result of sinking a ship. Expected results: ", "Destroyer" , model.p2Attack("A2"));
	}
	
	@Test
	/**
	 * Test the getSunkenShip function when the player has not sunk the opposing players ship.
	 */
	public void getInvalidSunkShip () {
		BattleshipModel model = new BattleshipModel();
		String result = "";
		model.placeShip(true, "A1", "A2", 4);
		assertEquals("Return the result of not sinking a ship. Expected results: ", "HIT" , model.p2Attack("A2"));
	}
	
	@Test
	/**
	 * Test the isGameOver function to check if the game is over once all ships are destroyed.
	 */
	public void gameIsOver () {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "A1", "A2", 4);
		model.placeShip(true, "B1", "B3", 3);
		model.placeShip(true, "C1", "C3", 2);
		model.placeShip(true, "D1", "D4", 1);
		model.placeShip(true, "E1", "E5", 0);
		for (int i = 1; i < 3; i++) {
			model.p2Attack("A"+i);
		}
		for (int i = 1; i < 4; i++) {
			model.p2Attack("B"+i);
		}
		for (int i = 1; i < 4; i++) {
			model.p2Attack("C"+i);
		}
		for (int i = 1; i < 5; i++) {
			model.p2Attack("D"+i);
		}
		for (int i = 1; i < 6; i++) {
			model.p2Attack("E"+i);
		}
		assertEquals("Return if the game is over when all of a player's ships are sunk. Expected results: ", true , model.isGameOver(false));
	}
	
	@Test
	/**
	 * Test the isGameOver function to check if the game is over if all ships are not destroyed.
	 */
	public void gameIsNotOver () {
		BattleshipModel model = new BattleshipModel();
		String result = "";
		model.placeShip(true, "A1", "A2", 4);
		assertEquals("Return if the game is over when there are still ships left. Expected results: ", false , model.isGameOver(false));
	}
	
	@Test
	/**
	 * Test the getSunkCells function to return the values of a sunken ship. 
	 */
	public void getValidSunkCells() {
		BattleshipModel model = new BattleshipModel();
		String name = "Destroyer";
		model.placeShip(true, "A1", "A2", 4);
		assertEquals("Return the cells of the sunken ship. Expected results: ", "[A1, A2]" , model.getSunkCells(false, "Destroyer").toString());
	}
	
	@Test
	/**
	 * Test the getSunkCells function to return the array of a non-sunken ship. 
	 */
	public void getInvalidSunkCells() {
		BattleshipModel model = new BattleshipModel();
		String name = "";
		assertEquals("Return the cells of a non-sunken ship. Expected results: ", "[]" , model.getSunkCells(false, "Destroyer").toString());
	}
	
	@Test
	/**
	 * Test the getValidRemainingCells function to return all cells that the player still has. 
	 */
	public void getValidRemainingCells () {
		BattleshipModel model = new BattleshipModel();
		model.placeShip(true, "A1", "A2", 4);
		assertEquals("Return the cells of the remaining ships. Expected results: ", "[A1, A2]" , model.getSurvivingCells(true).toString());
	}
	
}
